using System;
using System.IO;
using iTextSharp.text.pdf;
using System.Text.RegularExpressions;

namespace PdfToText
{
    public class PDFParser 
    {
        private static int CharsToKeep = 15;

        public string ExtractText(string url)
        {
            Uri pdf_url = new Uri(url);
            string final_result = "";

            try
            {
                PdfReader reader = new PdfReader(pdf_url);
              
                for (int page = 1; page <= reader.NumberOfPages; page++)
                    final_result += ExtractTextFromPDFBytes(reader.GetPageContent(page)) + " ";
            }
            catch {}

            return final_result;
        }

        private string ExtractTextFromPDFBytes(byte[] input)
        {
            if (input == null || input.Length == 0) return "";

            if (input.Length < 40) return "Bad PDF"; //TODO: De-hard code this.

            string result = "";

            string recentChars = "";
            for (int i = 0; i < CharsToKeep; i++) recentChars += " ";

            bool partOfText = false;
            bool partOfWord = false;
            bool isSingleSymbol = true;

            foreach (byte b in input)
            {
                char c = (char)b;

                if (partOfText)
                {
                    if (!partOfWord)
                    {
                        result = CheckForSpaces(result, recentChars);

                        if (recentChars.EndsWith("ET"))
                            partOfText = false;
                        if (c == '(') 
                            partOfWord = true;
                    }
                    else
                    {
                        if (c == ')')
                            partOfWord = false;
                        else if (c == '\\') 
                            isSingleSymbol = false;
                        else 
                        {
                            if (isSingleSymbol)
                                result = AddCharToResult(result, recentChars, c);
                            else if (isEndOfSymbol(c, recentChars))
                            {
                                isSingleSymbol = true;
                                result += ParseSymbol(c, recentChars);
                            }
                        }
                    }
                    result = AdjustResultForTex(result);
                }
                recentChars = UpdateRecentChars(recentChars, c);

                if (!partOfText)
                {
                    if (recentChars.EndsWith("BT"))
                        partOfText = true;
                }
            }
            return result;
        }


        private string ParseSymbol(char c, string recentChars)
        {
            string input = (recentChars + c).Substring((recentChars + c).Length - 3);

            switch (input)
            {
                case "013": return "ff";
                case "014": return "fi";
                case "050": return "(";
                case "051": return ")";
                case "002": return "X";
                case "134": return "\"";
                case "000": return "-";
                case "016": return "degrees";
                case "001": return ".";
                case "024": return "<=";
                case "025": return ">=";         
            }
            return "?";
        }

        private bool isEndOfSymbol(char c, string recent)
        {
            string recentChars = recent.Substring(recent.Length - 2);
            recentChars += c;
            Match match = Regex.Match(recentChars, "\\d\\d\\d");
            if (match.Success) return true;

            return false;
        }
            
        private string UpdateRecentChars(string recentChars, char c)
        {
            string output = recentChars;

            output = output.Substring(1, CharsToKeep - 1);
            output += c;

            return output;
        }

        private string CheckForSpaces(string result, string recentChars)
        {
            string output = result;

            if (isSpace(recentChars))
                if (!output.EndsWith(" ")) //Prevents multiple spaces 
                    output += " ";

            return output;
        }

        private string AddCharToResult(string result, string recentChars, char c)
        {
            string output = result;
               
            if (((c >= ' ') && (c <= '~')) || ((c >= 128) && (c < 255)))
                output += c.ToString();

            return output;
        }

        private string AdjustResultForTex(string result)
        {
            string output = result;

            output = ReplaceResult(":::", "...", output);
            output = ReplaceResult(": : :", "...", output);
            
            return output;
        }

        private bool isSpace(string recentChars)
        {
            string input = recentChars.Substring(recentChars.Length - 3);
            Match match = Regex.Match(input, "\\d\\d\\d");
            if (match.Success) return true;

            return false;
        }

        private string ReplaceResult(string toReplace, string replaceWith, string result)
        {
            string output = result;

            if (output.EndsWith(toReplace))
            {
                output = output.Remove(result.Length - toReplace.Length);
                output += replaceWith;
            }

            return output;
        }
    }
}
